﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Godot.core;

namespace Godot.modules.gdscript
{
    public partial class GDTokenizerText : GDTokenizer
    {
        protected TokenData[] _tk_rb;
        protected int _tk_rb_pos;
        protected string _code;
	    protected int _len;
	    protected int _code_pos;
	    protected char[] code_data;
	    protected int _line;
	    protected int _column;	    
	    protected string _last_error;
	    protected bool _error_flag;

        public void set_code(String p_code)
        {
            _code = p_code;
            _len = p_code.Length;
            
            if(_len > 0)
            {
                code_data = _code.ToArray();
            }
            else
            {
                code_data = null;
            }
            
            _code_pos = 0;
            _line = 1; //it is stand-ar-ized that lines begin in 1 in code..
            _column = 0;
            _tk_rb_pos = 0;
            _error_flag = false;
            _last_error = "";
            
            for (int i = 0; i < MAX_LOOKAHEAD + 1; i++)
                _advance();
        }

        public override Variant get_token_constant(int p_offset = 0)
        {
            if(p_offset <= -MAX_LOOKAHEAD)
                return _tk_rb[0].constant;

            if(p_offset >= MAX_LOOKAHEAD) 
                return _tk_rb[0].constant;

            int ofs = (TK_RB_SIZE + _tk_rb_pos + p_offset - MAX_LOOKAHEAD - 1) % TK_RB_SIZE;

            if(_tk_rb[ofs].type != GDTokenizer.Token.TK_CONSTANT) 
                return _tk_rb[0].constant;
            return _tk_rb[ofs].constant;
        }

        public override GDTokenizer.Token get_token(int p_offset = 0)
        {
            if(p_offset <= -MAX_LOOKAHEAD)
                return GDTokenizer.Token.TK_ERROR;

            if(p_offset >= MAX_LOOKAHEAD) 
                return GDTokenizer.Token.TK_ERROR;

            int ofs = (TK_RB_SIZE + _tk_rb_pos + p_offset - MAX_LOOKAHEAD - 1) % TK_RB_SIZE;
            return _tk_rb[ofs].type;
        }

        public override StringName get_token_identifier(int p_offset = 0)
        {
            if( p_offset <= -MAX_LOOKAHEAD) 
                return new StringName();
	
            if(p_offset >= MAX_LOOKAHEAD) 
                return new StringName();

	        int ofs = (TK_RB_SIZE + _tk_rb_pos + p_offset - MAX_LOOKAHEAD -1) % TK_RB_SIZE;

	        if(_tk_rb[ofs].type != GDTokenizer.Token.TK_IDENTIFIER)
                return new StringName();

	        return _tk_rb[ofs].identifier;
        }

        public override GDFunctions.Function get_token_built_in_func(int p_offset = 0)
        {
            if( p_offset <= -MAX_LOOKAHEAD)
                return GDFunctions.Function.FUNC_MAX;

	        if( p_offset >= MAX_LOOKAHEAD)
                return GDFunctions.Function.FUNC_MAX;

	        int ofs = (TK_RB_SIZE + _tk_rb_pos + p_offset - MAX_LOOKAHEAD -1) % TK_RB_SIZE;

	        if(_tk_rb[ofs].type != GDTokenizer.Token.TK_BUILT_IN_FUNC)
                return GDFunctions.Function.FUNC_MAX;
	        
            return _tk_rb[ofs].func;
        }

        public override Variant.Type get_token_type(int p_offset = 0)
        {           
	        if( p_offset <= -MAX_LOOKAHEAD)
                return Variant.Type.NIL;
	        
            if( p_offset >= MAX_LOOKAHEAD) 
                return Variant.Type.NIL;

	        int ofs = (TK_RB_SIZE + _tk_rb_pos + p_offset - MAX_LOOKAHEAD -1) % TK_RB_SIZE;
	        
            if(_tk_rb[ofs].type != GDTokenizer.Token.TK_BUILT_IN_TYPE)
                return Variant.Type.NIL;
	        
            return _tk_rb[ofs].vtype;
        }

        public override int get_token_line(int p_offset = 0)
        {
            if(p_offset <= -MAX_LOOKAHEAD)
                return -1;

            if(p_offset >= MAX_LOOKAHEAD) 
                return -1;

            int ofs = (TK_RB_SIZE + _tk_rb_pos + p_offset - MAX_LOOKAHEAD - 1) % TK_RB_SIZE;
            return _tk_rb[ofs].line;
        }

        public override int get_token_column(int p_offset = 0)
        {
            if(p_offset <= -MAX_LOOKAHEAD)
                return -1;

            if(p_offset >= MAX_LOOKAHEAD) 
                return -1;

            int ofs = (TK_RB_SIZE + _tk_rb_pos + p_offset - MAX_LOOKAHEAD - 1) % TK_RB_SIZE;
            
            return _tk_rb[ofs].col;
        }

        public override int get_token_line_indent(int p_offset = 0)
        {
            if(p_offset <= -MAX_LOOKAHEAD) 
                return 0;
            
            if(p_offset >= MAX_LOOKAHEAD) 
                return 0;

            int ofs = (TK_RB_SIZE + _tk_rb_pos + p_offset - MAX_LOOKAHEAD - 1) % TK_RB_SIZE;
            
            if(_tk_rb[ofs].type != GDTokenizer.Token.TK_NEWLINE) 
                return 0;

            return _tk_rb[ofs].constant;
        }

        public override string get_token_error(int p_offset = 0)
        {
            if(p_offset <= -MAX_LOOKAHEAD) 
                return "";

            if(p_offset >= MAX_LOOKAHEAD) 
                return "";

            int ofs = (TK_RB_SIZE + _tk_rb_pos + p_offset - MAX_LOOKAHEAD - 1) % TK_RB_SIZE;
            
            if(_tk_rb[ofs].type != GDTokenizer.Token.TK_ERROR)
                return "";
            
            return _tk_rb[ofs].constant;
        }

        public override void advance(int p_amount = 1)
        {
            if (p_amount <= 0) 
                return;

            for (int i = 0; i < p_amount; i++)
                _advance();
        }
    }
}
